home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / scrollingeditfield / stupcontrol.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  38.9 KB  |  991 lines

  1. /*
  2.     File: STUPControl.c
  3.     
  4.     Description:
  5.         STUPControl implementation.
  6.  
  7.     Copyright:
  8.         © Copyright 2000 Apple Computer, Inc. All rights reserved.
  9.     
  10.     Disclaimer:
  11.         IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  12.         ("Apple") in consideration of your agreement to the following terms, and your
  13.         use, installation, modification or redistribution of this Apple software
  14.         constitutes acceptance of these terms.  If you do not agree with these terms,
  15.         please do not use, install, modify or redistribute this Apple software.
  16.  
  17.         In consideration of your agreement to abide by the following terms, and subject
  18.         to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
  19.         copyrights in this original Apple software (the "Apple Software"), to use,
  20.         reproduce, modify and redistribute the Apple Software, with or without
  21.         modifications, in source and/or binary forms; provided that if you redistribute
  22.         the Apple Software in its entirety and without modifications, you must retain
  23.         this notice and the following text and disclaimers in all such redistributions of
  24.         the Apple Software.  Neither the name, trademarks, service marks or logos of
  25.         Apple Computer, Inc. may be used to endorse or promote products derived from the
  26.         Apple Software without specific prior written permission from Apple.  Except as
  27.         expressly stated in this notice, no other rights or licenses, express or implied,
  28.         are granted by Apple herein, including but not limited to any patent rights that
  29.         may be infringed by your derivative works or by other works in which the Apple
  30.         Software may be incorporated.
  31.  
  32.         The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  33.         WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  34.         WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  35.         PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  36.         COMBINATION WITH YOUR PRODUCTS.
  37.  
  38.         IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  39.         CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  40.         GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  41.         ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  42.         OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
  43.         (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
  44.         ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  45.  
  46.     Change History (most recent first):
  47.         Fri, Jan 28, 2000 -- created
  48. */
  49.  
  50.  
  51.  
  52.  
  53. #include "STUPControl.h"
  54. #include <ControlDefinitions.h>
  55. #include <Resources.h>
  56. #include <Scrap.h>
  57. #include <TextEdit.h>
  58.  
  59. enum {
  60.     kShiftKeyCode = 56
  61. };
  62.  
  63. /* kUserClickedToFocusPart is a part code we pass to the SetKeyboardFocus
  64.     routine.  In our focus switching routine this part code is understood
  65.     as meaning 'the user has clicked in the control and we need to switch
  66.     the current focus to ourselves before we can continue'. */
  67. #define kUserClickedToFocusPart 100
  68.  
  69.  
  70. /* kSTUPClickScrollDelayTicks is a time measurement in ticks used to
  71.     slow the speed of 'auto scrolling' inside of our clickloop routine.
  72.     This value prevents the text from wizzzzzing by while the mouse
  73.     is being held down inside of the text area. */
  74. #define kSTUPClickScrollDelayTicks 3
  75.  
  76.  
  77. /* STPTextPaneVars is a structure used for storing the the STUP Control's
  78.     internal variables and state information.  A handle to this record is
  79.     stored in the pane control's reference value field using the
  80.     SetControlReference routine. */
  81.  
  82. typedef struct {
  83.         /* OS records referenced */
  84.     ControlHandle fScrollBarRec; /* a reference to the scroll bar */
  85.     TEHandle fTextEditRec; /* the text edit record */
  86.     ControlHandle fUserPaneRec;  /* handle to the user pane control */
  87.     WindowPtr fOwner; /* window containing control */
  88.     CGrafPtr fDrawingEnvironment; /* grafport where control is drawn */
  89.         /* flags */
  90.     Boolean fInFocus; /* true while the focus rect is drawn around the control */
  91.     Boolean fIsActive; /* true while the control is drawn in the active state */
  92.     Boolean fTEActive; /* reflects the activation state of the text edit record */ 
  93.         /* calculated locations */
  94.     Rect fRTextArea; /* area where the text is drawn */
  95.     Rect fRTextViewMax; /* a subregion inside of fRTextArea where the text is actually drawn
  96.                     The text edit's view rectangle is calculated using this rectangle. */
  97.     Rect fRScrollView; /* rectangle where the scroll bar is drawn */
  98.     Rect fRFocusOutline;  /* rectangle used to draw the focus box */
  99.     Rect fRTextOutline; /* rectangle used to draw the border */
  100.     RgnHandle fTextBackgroundRgn; /* background region for the text, erased before calling TEUpdate */
  101. } STPTextPaneVars;
  102.  
  103.  
  104.  
  105.  
  106. /* Univerals Procedure Pointer variables used by the
  107.     STUP Control.  These variables are set up
  108.     the first time that STUPOpenControl is called. */
  109. ControlUserPaneDrawUPP gTPDrawProc = NULL;
  110. ControlUserPaneHitTestUPP gTPHitProc = NULL;
  111. ControlUserPaneTrackingUPP gTPTrackProc = NULL;
  112. ControlUserPaneIdleUPP gTPIdleProc = NULL;
  113. ControlUserPaneKeyDownUPP gTPKeyProc = NULL;
  114. ControlUserPaneActivateUPP gTPActivateProc = NULL;
  115. ControlUserPaneFocusUPP gTPFocusProc = NULL;
  116. TEClickLoopUPP gTPClickLoopProc = NULL;
  117.  
  118.  
  119.  
  120.  
  121. /* TPActivatePaneText activates or deactivates the text edit record
  122.     according to the value of setActive.  The primary purpose of this
  123.     routine is to ensure each call is only made once. */
  124. static void TPActivatePaneText(STPTextPaneVars **tpvars, Boolean setActive) {
  125.     STPTextPaneVars *varsp;
  126.     varsp = *tpvars;
  127.     if (varsp->fTEActive != setActive) {
  128.         varsp->fTEActive = setActive;
  129.         if (varsp->fTEActive)
  130.             TEActivate(varsp->fTextEditRec);
  131.         else TEDeactivate(varsp->fTextEditRec);
  132.     }
  133. }
  134.  
  135.  
  136.  
  137. /* TPPaneDrawEntry and TPPaneDrawExit are utility routines used for
  138.     saving and restoring the port's drawing color and pen mode.  They
  139.     are intended to be used around all calls to text edit that may
  140.     draw something on the screen.  These routines ensure that the
  141.     text will be drawn black with a white background. */
  142.  
  143.  
  144. /* STPPaneState is used to store the drawing enviroment information */
  145.  
  146. typedef struct {
  147.     RGBColor sForground, sBackground;
  148.     PenState sPen;
  149. } STPPaneState;
  150.  
  151.  
  152. /* TPPaneDrawEntry sets the current grafport to the STUP control's
  153.     grafport and it sets up the drawing colors in preparation for
  154.     drawing the text field (after saving the current drawing colors). */
  155. static void TPPaneDrawEntry(STPTextPaneVars **tpvars, STPPaneState *ps) {
  156.     RGBColor rgbWhite = {0xFFFF, 0xFFFF, 0xFFFF}, rgbBlack = {0, 0, 0};
  157.         /* set the port to our window */
  158.     SetPort((**tpvars).fDrawingEnvironment);
  159.         /* save the current drawing colors */
  160.     GetForeColor(&ps->sForground);
  161.     GetBackColor(&ps->sBackground);
  162.         /* set the drawing colors to black and white */
  163.     RGBForeColor(&rgbBlack);
  164.     RGBBackColor(&rgbWhite);
  165.         /* save the pen state.  Paranoia?  what?  What? */
  166.     GetPenState(&ps->sPen);
  167. }
  168.  
  169. /* TPPaneDrawExit should be called after TPPaneDrawEntry.  This
  170.     routine restores the drawing colors that were saved away
  171.     by TPPaneDrawEntry. */
  172. static void TPPaneDrawExit(STPPaneState *ps) {
  173.         /* restore the colors and the pen state */
  174.     RGBForeColor(&ps->sForground);
  175.     RGBBackColor(&ps->sBackground);
  176.     SetPenState(&ps->sPen);
  177. }
  178.  
  179.  
  180.  
  181.  
  182. /* TPRecalculateTextParams is called after any routine that modifies the contents or
  183.     the appearance of the text being displayed in the text area.  if activeUpdate is
  184.     true, then this routine recalculates the text and some of the diaplay characteristics.
  185.     This flag should be used when the font, style, or size has been changed.
  186.     When activeUpdate is false, this routine simply updates the value of the
  187.     scroll bar. */
  188. static void TPRecalculateTextParams(STPTextPaneVars **tpvars, Boolean activeUpdate) {
  189.     short value, max, spaceabove, error;
  190.     Rect vr, dr;
  191.     STPPaneState ps;
  192.     STPTextPaneVars *varsp;
  193.     char state;
  194.         /* set up locals */
  195.     state = HGetState((Handle) tpvars);
  196.     HLock((Handle) tpvars);
  197.     varsp = *tpvars;
  198.         /* set up locals */
  199.     vr = (**varsp->fTextEditRec).viewRect;
  200.     dr = (**varsp->fTextEditRec).destRect;
  201.     spaceabove = vr.top - dr.top;
  202.     max = (**varsp->fTextEditRec).nLines - 1;
  203.     if (max < 0) max = 0;
  204.         /* update scroll values */
  205.     if (activeUpdate) {
  206.             /* recalculate and redraw the text */
  207.         TECalText(varsp->fTextEditRec);
  208.         InvalWindowRect(varsp->fOwner, &varsp->fRTextViewMax);
  209.             /* make sure the view rect bottom is aligned on a line boundary */
  210.         error = ((varsp->fRTextViewMax.bottom - varsp->fRTextViewMax.top) % (**varsp->fTextEditRec).lineHeight);
  211.         if (error != 0) {
  212.             (**varsp->fTextEditRec).viewRect = varsp->fRTextViewMax;
  213.             (**varsp->fTextEditRec).viewRect.bottom -= error;
  214.         }
  215.             /* align the text with the control value */
  216.         value = GetControlValue(varsp->fScrollBarRec);
  217.         if (value > max) SetControlValue(varsp->fScrollBarRec, (value = max));
  218.             /* re-align the text on a line boundary */
  219.         if ((value *  (**varsp->fTextEditRec).lineHeight) != spaceabove) {
  220.             TPPaneDrawEntry(tpvars, &ps);
  221.             TEScroll(0, spaceabove - (value * (**varsp->fTextEditRec).lineHeight), varsp->fTextEditRec);
  222.             TPPaneDrawExit(&ps);
  223.         }
  224.             /* set the view size so that proportional scroll bars are displayed
  225.             correctly.  With this control, the range of values (i.e. min...max) is
  226.             the number of lines of text.  The "ViewSize" being set in the
  227.             following call is the number of lines of text visible on the screen
  228.             inside of the text edit record's viewRect. */
  229.         SetControlViewSize(varsp->fScrollBarRec, 
  230.             ((**varsp->fTextEditRec).viewRect.bottom - (**varsp->fTextEditRec).viewRect.top) / (**varsp->fTextEditRec).lineHeight);
  231.     } else {
  232.             /* align the scroll value */
  233.         value = spaceabove / (**varsp->fTextEditRec).lineHeight;
  234.         if (GetControlValue(varsp->fScrollBarRec) != value) SetControlValue(varsp->fScrollBarRec, value);
  235.     }
  236.         /* make sure the displayed maximum is within allowable bounds */
  237.     if (GetControlMaximum(varsp->fScrollBarRec) != max) SetControlMaximum(varsp->fScrollBarRec, max);
  238.     HSetState((Handle) tpvars, state);
  239. }
  240.  
  241.  
  242. /* TPPaneDrawProc is called to redraw the control and for update events
  243.     referring to the control.  This routine erases the text area's background,
  244.     and redraws the text.  This routine assumes the scroll bar has been
  245.     redrawn by a call to DrawControls. */
  246. static pascal void TPPaneDrawProc(ControlRef theControl, ControlPartCode thePart) {
  247.     STPPaneState ps;
  248.     STPTextPaneVars **tpvars;
  249.     char state;
  250.         /* set up our globals */
  251.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  252.     if (tpvars != NULL) {
  253.         state = HGetState((Handle) tpvars);
  254.         HLock((Handle) tpvars);
  255.             /* save the drawing state */
  256.         TPPaneDrawEntry(tpvars, &ps);
  257.             /* update the text region */
  258.         EraseRgn((**tpvars).fTextBackgroundRgn);
  259.         TEUpdate(&(**tpvars).fRTextViewMax, (**tpvars).fTextEditRec);
  260.             /* restore the drawing environment */
  261.         TPPaneDrawExit(&ps);
  262.             /* draw the text frame and focus frame (if necessary) */
  263.         DrawThemeEditTextFrame(&(**tpvars).fRTextOutline, (**tpvars).fIsActive ? kThemeStateActive: kThemeStateInactive);
  264.         if ((**tpvars).fIsActive && (**tpvars).fInFocus) DrawThemeFocusRect(&(**tpvars).fRFocusOutline, true);
  265.             /* release our globals */
  266.         HSetState((Handle) tpvars, state);
  267.     }
  268. }
  269.  
  270.  
  271. /* TPPaneHitTestProc is called when the control manager would
  272.     like to determine what part of the control the mouse resides over.
  273.     We also call this routine from our tracking proc to determine how
  274.     to handle mouse clicks. */
  275. static pascal ControlPartCode TPPaneHitTestProc(ControlHandle theControl, Point where) {
  276.     STPTextPaneVars **tpvars;
  277.     ControlPartCode result;
  278.     char state;
  279.         /* set up our locals and lock down our globals*/
  280.     result = 0;
  281.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  282.     if (tpvars != NULL) {
  283.         state = HGetState((Handle) tpvars);
  284.         HLock((Handle) tpvars);
  285.             /* find the region where we clicked */
  286.         if (PtInRect(where, &(**tpvars).fRTextArea)) {
  287.             result = kSTUPTextPart;
  288.         } else if (PtInRect(where, &(**tpvars).fRScrollView)) {
  289.             result = kSTUPScrollPart;
  290.         } else result = 0;
  291.             /* release oure globals */
  292.         HSetState((Handle) tpvars, state);
  293.     }
  294.     return result;
  295. }
  296.  
  297.  
  298.  
  299. /* gPreviousThumbValue is a global variable used for tracking the
  300.     last 'drawn' position for the text in the scrolling edit field
  301.     while the user is dragging the control's indicator. It is set
  302.     immediately before TrackControl and is used inside of the
  303.     TPScrollActionProc routine defined below. */
  304. static short gPreviousThumbValue;
  305.  
  306.  
  307. /* TPScrollActionProc is the control action procedure used by the
  308.     scroll bar. this routine is used for all text scrolling operations. */
  309. static pascal void TPScrollActionProc(ControlRef theControl, ControlPartCode partCode) {
  310.     short next, prev, max, onePage, dv;
  311.     STPPaneState ps;
  312.     STPTextPaneVars **tpvars, *varsp;
  313.     char state;
  314.         /* lock down and dereference our globals. We stored a copy of them in
  315.         the scroll bar's reference field when the scroll bar was created. */
  316.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  317.     state = HGetState((Handle) tpvars);
  318.     HLock((Handle) tpvars);
  319.     varsp = *tpvars;
  320.         /* we do special processing for the indicator part to support
  321.         "Live Scrolling".  It's handled like one of those state to state
  322.         things.  In the way it has been done here, gPreviousThumbValue
  323.         will always hold the previous scrolling position.  Because
  324.         of that, our scrolling routine will always know 'how much to scroll'
  325.         during live scrolling operations. To identify live scrolling operations,
  326.         the control manager hands us the kControlIndicatorPart part code.*/
  327.     if (partCode == kControlIndicatorPart) {
  328.         prev = gPreviousThumbValue;
  329.         gPreviousThumbValue = next = GetControlValue(theControl);
  330.     } else {
  331.             /* of couse, the more traditional, yet renamed, part
  332.             codes are handled in the traditional ways. For more information,
  333.             see the human interface guidelines.  Trust me, this is done
  334.             the right way.... */
  335.         prev = GetControlValue(theControl);
  336.         switch (partCode) {
  337.             default: next = prev; break;
  338.             case kControlUpButtonPart: next = prev - 1; break;
  339.             case kControlDownButtonPart: next = prev + 1; break;
  340.             case kControlPageUpPart:
  341.                 onePage = (((**varsp->fTextEditRec).viewRect.bottom - (**varsp->fTextEditRec).viewRect.top) / (**varsp->fTextEditRec).lineHeight);
  342.                 next = prev - onePage;
  343.                 break;
  344.             case kControlPageDownPart:
  345.                 onePage = (((**varsp->fTextEditRec).viewRect.bottom - (**varsp->fTextEditRec).viewRect.top) / (**varsp->fTextEditRec).lineHeight);
  346.                 next = prev + onePage;
  347.                 break;
  348.         }
  349.     }
  350.         /* verify the next value we've calculated is within the allowable bounds */
  351.     max = GetControlMaximum(theControl);
  352.     if (next < 0) next = 0; else if (next > max) next = max;
  353.         /* now that we know the next value we want is *totally acceptable*
  354.         make sure we'll be making a difference */
  355.     if (prev != next) {
  356.             /* calculate the difference */
  357.         dv = prev - next;
  358.             /* scroll the text */
  359.         TPPaneDrawEntry(tpvars, &ps);
  360.         TEScroll(0, (dv * (**varsp->fTextEditRec).lineHeight), varsp->fTextEditRec);
  361.         TPPaneDrawExit(&ps);
  362.             /* set the new control value, only if we're going to make a visual difference */
  363.         if (GetControlValue(theControl) != next)
  364.             SetControlValue(theControl, next);
  365.     }
  366.         /* unlock our globals and leave */
  367.     HSetState((Handle) tpvars, state);
  368. }
  369.  
  370.  
  371.  
  372. /* gTPClickedUserPaneVars is a global variable we
  373.     set before calling TEClick.  This variable is used
  374.     as a parameter to the TPTEClickLoopProc which
  375.     is called by Text Edit during TEClick calls. */
  376. STPTextPaneVars **gTPClickedUserPaneVars;
  377.  
  378.  
  379. /* TPTEClickLoopProc is our custom text edit clickLoop
  380.     routine that we install in the text edit record when
  381.     it is created.  This routine is called during TEClick
  382.     by Text Edit, and it provides opportunity for us
  383.     to implement automatic scrolling.  Here, we scroll the
  384.     text when the mouse moves beyond the top or bottom
  385.     of the text's view rectangle. */
  386. static pascal Boolean TPTEClickLoopProc(TEPtr pTE) {
  387.     Point mouse;
  388.     RgnHandle clipsave, nclip;
  389.     Rect bounds;
  390.     unsigned long finalTicks;
  391.     char state;
  392.     STPTextPaneVars *varsp;
  393.         /* lock down and dereference our globals. */
  394.     state = HGetState((Handle) gTPClickedUserPaneVars);
  395.     HLock((Handle) gTPClickedUserPaneVars);
  396.     varsp = *gTPClickedUserPaneVars;
  397.         /* we can assume that the current port is the port containing
  398.         our text at this point, so it's safe to call GetMouse without any
  399.         preamble. */
  400.     GetMouse(&mouse);
  401.         /* at this point, text edit has adjusted the clip region so that
  402.         it contains nothing but the text edit view rectangle.  The next
  403.         few lines expand the clip region to include the scroll bar so if
  404.         we set that value, it will be redrawn too */
  405.     GetClip((clipsave = NewRgn()));
  406.     GetControlBounds(varsp->fScrollBarRec, &bounds);
  407.     RectRgn((nclip = NewRgn()), &bounds);
  408.     UnionRgn(nclip, clipsave, nclip);
  409.     SetClip(nclip);
  410.         /* call the scroll bar action procedure to do the scrolling.  Why
  411.         do things twice?  we already have a perfectly good routine
  412.         that does scrolling in a really cool way. */
  413.     if (mouse.v < (**varsp->fTextEditRec).viewRect.top)
  414.         TPScrollActionProc(varsp->fScrollBarRec, kControlUpButtonPart);
  415.     else if (mouse.v > (**varsp->fTextEditRec).viewRect.bottom)
  416.         TPScrollActionProc(varsp->fScrollBarRec, kControlDownButtonPart);
  417.         /* the next line is included for historical reasons, but it doesn't
  418.         work. */
  419.     (**varsp->fTextEditRec).clickTime = (long) TickCount();
  420.         /* because the previous line didn't work, I added the next line */
  421.     Delay( kSTUPClickScrollDelayTicks, &finalTicks);
  422.         /* restore the clip region to whatever textedit was using. */
  423.     SetClip(clipsave);
  424.     DisposeRgn(clipsave);
  425.     DisposeRgn(nclip);
  426.         /* unlock our variables */
  427.     HSetState((Handle) gTPClickedUserPaneVars, state);
  428.         /* techinically, this routine returns true if we should continue
  429.         tracking the mouse, and false if we should not. */
  430.     return StillDown();
  431. }
  432.  
  433.  
  434. /* TPPaneTrackingProc is called when the mouse is being held down
  435.     over our control.  This routine handles clicks in the text area
  436.     and in the scroll bar. */
  437. static pascal ControlPartCode TPPaneTrackingProc(ControlHandle theControl, Point startPt, ControlActionUPP actionProc) {
  438.     STPTextPaneVars **tpvars, *varsp;
  439.     char state;
  440.     ControlPartCode partCodeResult;
  441.         /* make sure we have some variables... */
  442.     partCodeResult = 0;
  443.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  444.     if (tpvars != NULL) {
  445.             /* lock 'em down */
  446.         state = HGetState((Handle) tpvars);
  447.         HLock((Handle) tpvars);
  448.         varsp = *tpvars;
  449.             /* we don't do any of these functions unless we're in focus */
  450.         if ( ! varsp->fInFocus) {
  451.             WindowPtr owner;
  452.             owner = GetControlOwner(theControl);
  453.             ClearKeyboardFocus(owner);
  454.             SetKeyboardFocus(owner, theControl, kUserClickedToFocusPart);
  455.         }
  456.             /* find the location for the click */
  457.         switch (TPPaneHitTestProc(theControl, startPt)) {
  458.                 
  459.                 /* handle clicks in the text part */
  460.             case kSTUPTextPart:
  461.                 {    STPPaneState ps;
  462.                     KeyMapByteArray theKeys;
  463.                     Boolean shiftKeyDown;
  464.                         /* set the cursor to the I beam cursor incase
  465.                         we were not focused and the cursor was something else */
  466.                     SetThemeCursor(kThemeIBeamCursor);
  467.                         /* handle the click */
  468.                     TPPaneDrawEntry(tpvars, &ps);
  469.                         /* TPTEClickLoopProc uses the gTPClickedUserPaneVars variable
  470.                         to find the text edit record that was clicked on.  Since TPTEClickLoopProc
  471.                         is called inside of TEClick, we set that variable first... */
  472.                         /* check if the shift key is down */
  473.                     GetKeys((void*) theKeys);
  474.                     shiftKeyDown = ((theKeys[kShiftKeyCode/8] & (1<<(kShiftKeyCode%8))) != 0);
  475.                         /* process the click */
  476.                     gTPClickedUserPaneVars = tpvars;
  477.                     TEClick(startPt, shiftKeyDown, varsp->fTextEditRec);
  478.                     TPPaneDrawExit(&ps);
  479.                         /* indeed, we just clicked in the text part... yes... */
  480.                     partCodeResult = kSTUPTextPart;
  481.                 }
  482.                 break;
  483.             
  484.                 /* scroll bar clicks */
  485.             case kSTUPScrollPart:
  486.                 {    ControlActionUPP actionp;
  487.                     Boolean trackingThumb;
  488.                     trackingThumb = false;
  489.                     actionp = NewControlActionUPP(TPScrollActionProc);
  490.                         /* if we're clicking in the indicator part, then we do
  491.                         __some__ special processing.  Namely, we save the current
  492.                         value of the control in the global variable gPreviousThumbValue
  493.                         __before__ calling TrackControl and we set the cursor to the
  494.                         closed hand so it looks like we're grabbing the thumb. */
  495.                     if (TestControl(varsp->fScrollBarRec, startPt) == kControlIndicatorPart) {
  496.                         SetThemeCursor(kThemeClosedHandCursor);
  497.                         gPreviousThumbValue = GetControlValue(varsp->fScrollBarRec);
  498.                         trackingThumb = true;
  499.                     }
  500.                         /* handle the mouse click */
  501.                     TrackControl(varsp->fScrollBarRec, startPt, actionp);
  502.                         /* if we were grabbing the thumb, then set the cursor
  503.                         back to the open hand so it looks like we let go. */
  504.                     if (trackingThumb)
  505.                         SetThemeCursor(kThemeOpenHandCursor);
  506.                         /* clean up storage we allocated and leave */
  507.                     DisposeControlActionUPP(actionp);
  508.                         /* yes, we clicked and handled a click in the kSTUPScrollPart part */
  509.                     partCodeResult = kSTUPScrollPart;
  510.                 }
  511.                 break;
  512.         }
  513.         
  514.         HSetState((Handle) tpvars, state);
  515.     }
  516.     return partCodeResult;
  517. }
  518.  
  519.  
  520. /* TPPaneIdleProc is our user pane idle routine.  When our text field
  521.     is active and in focus, we use this routine to set the cursor. */
  522. static pascal void TPPaneIdleProc(ControlHandle theControl) {
  523.     STPTextPaneVars **tpvars, *varsp;
  524.         /* set up locals */
  525.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  526.     if (tpvars != NULL) {
  527.             /* if we're not active, then we have nothing to say about the cursor */
  528.         if ((**tpvars).fIsActive) {
  529.             char state;
  530.             Rect bounds;
  531.             Point mousep;
  532.                 /* lock down the globals */
  533.             state = HGetState((Handle) tpvars);
  534.             HLock((Handle) tpvars);
  535.             varsp = *tpvars;
  536.                 /* get the current mouse coordinates (in our window) */
  537.             SetPort(GetWindowPort(GetControlOwner(theControl)));
  538.             GetMouse(&mousep);
  539.                 /* there's a 'focus thing' and an 'unfocused thing' */
  540.             if (varsp->fInFocus) {
  541.                 STPPaneState ps;
  542.                     /* flash the cursor */
  543.                 TPPaneDrawEntry(tpvars, &ps);
  544.                     TEIdle(varsp->fTextEditRec);
  545.                 TPPaneDrawExit(&ps);
  546.                     /* set the cursor */
  547.                 if (PtInRect(mousep, &varsp->fRTextArea)) {
  548.                     SetThemeCursor(kThemeIBeamCursor);
  549.                  } else if (PtInRect(mousep, &varsp->fRScrollView)) {
  550.                     if (TestControl(varsp->fScrollBarRec, mousep) == kControlIndicatorPart)
  551.                         SetThemeCursor(kThemeOpenHandCursor);
  552.                     else SetThemeCursor(kThemeArrowCursor);
  553.                 } else SetThemeCursor(kThemeArrowCursor);
  554.             } else {
  555.                 /* if it's in our bounds, set the cursor */
  556.                 GetControlBounds(theControl, &bounds);
  557.                 if (PtInRect(mousep, &bounds))
  558.                     SetThemeCursor(kThemeArrowCursor);
  559.             }
  560.             
  561.             HSetState((Handle) tpvars, state);
  562.         }
  563.     }
  564. }
  565.  
  566.  
  567. /* TPPaneKeyDownProc is called whenever a keydown event is directed
  568.     at our control.  Here, we direct the keydown event to the text
  569.     edit record and redraw the scroll bar and text field as appropriate. */
  570. static pascal ControlPartCode TPPaneKeyDownProc(ControlHandle theControl,
  571.                             SInt16 keyCode, SInt16 charCode, SInt16 modifiers) {
  572.     STPTextPaneVars **tpvars;
  573.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  574.     if (tpvars != NULL) {
  575.         if ((**tpvars).fInFocus) {
  576.             STPPaneState ps;
  577.                 /* turn autoscrolling on and send the key event to text edit */
  578.             TPPaneDrawEntry(tpvars, &ps);
  579.                 TEAutoView(true, (**tpvars).fTextEditRec);
  580.                 TEKey(charCode, (**tpvars).fTextEditRec);
  581.                 TEAutoView(false, (**tpvars).fTextEditRec);
  582.             TPPaneDrawExit(&ps);
  583.             TPRecalculateTextParams(tpvars, false);
  584.         }
  585.     }
  586.     return kControlEntireControl;
  587. }
  588.  
  589.  
  590. /* TPPaneActivateProc is called when the window containing
  591.     the user pane control receives activate events. Here, we redraw
  592.     the control and it's text as necessary for the activation state. */
  593. static pascal void TPPaneActivateProc(ControlHandle theControl, Boolean activating) {
  594.     Rect bounds;
  595.     STPPaneState ps;
  596.     STPTextPaneVars **tpvars, *varsp;
  597.     char state;
  598.         /* set up locals */
  599.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  600.     if (tpvars != NULL) {
  601.         state = HGetState((Handle) tpvars);
  602.         HLock((Handle) tpvars);
  603.         varsp = *tpvars;
  604.             /* de/activate the text edit record */
  605.         TPPaneDrawEntry(tpvars, &ps);
  606.             GetControlBounds(theControl, &bounds);
  607.             varsp->fIsActive = activating;
  608.             TPActivatePaneText(tpvars, varsp->fIsActive && varsp->fInFocus);
  609.         TPPaneDrawExit(&ps);
  610.             /* redraw the frame */
  611.         DrawThemeEditTextFrame(&varsp->fRTextOutline, varsp->fIsActive ? kThemeStateActive: kThemeStateInactive);
  612.         if (varsp->fInFocus) DrawThemeFocusRect(&varsp->fRFocusOutline, varsp->fIsActive);
  613.         HSetState((Handle) tpvars, state);
  614.     }
  615. }
  616.  
  617.  
  618. /* TPPaneFocusProc is called when every the focus changes to or
  619.     from our control.  Herein, switch the focus appropriately
  620.     according to the parameters and redraw the control as
  621.     necessary.  */
  622. static pascal ControlPartCode TPPaneFocusProc(ControlHandle theControl, ControlFocusPart action) {
  623.     STPPaneState ps;
  624.     ControlPartCode focusResult;
  625.     STPTextPaneVars **tpvars, *varsp;
  626.     char state;
  627.         /* set up locals */
  628.     focusResult = kControlFocusNoPart;
  629.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  630.     if (tpvars != NULL) {
  631.         state = HGetState((Handle) tpvars);
  632.         HLock((Handle) tpvars);
  633.         varsp = *tpvars;
  634.             /* if kControlFocusPrevPart and kControlFocusNextPart are received when the user is
  635.             tabbing forwards (or shift tabbing backwards) through the items in the dialog,
  636.             and kControlFocusNextPart will be received.  When the user clicks in our field
  637.             and it is not the current focus, then the constant kUserClickedToFocusPart will
  638.             be received.  The constant kControlFocusNoPart will be received when our control
  639.             is the current focus and the user clicks in another control.  In your focus routine,
  640.             you should respond to these codes as follows:
  641.  
  642.             kControlFocusNoPart - turn off focus and return kControlFocusNoPart.  redraw
  643.                 the control and the focus rectangle as necessary.
  644.  
  645.             kControlFocusPrevPart or kControlFocusNextPart - toggle focus on or off
  646.                 depending on its current state.  redraw the control and the focus rectangle
  647.                 as appropriate for the new focus state.  If the focus state is 'off', return the constant
  648.                 kControlFocusNoPart, otherwise return a non-zero part code.
  649.             kUserClickedToFocusPart - is a constant defined for this example.  You should
  650.                 define your own value for handling click-to-focus type events. */
  651.         switch (action) {
  652.             default:
  653.             case kControlFocusNoPart:
  654.                 varsp->fInFocus = false;
  655.                 focusResult = kControlFocusNoPart;
  656.                 break;
  657.             case kUserClickedToFocusPart:
  658.                 varsp->fInFocus = true;
  659.                 focusResult = 1;
  660.                 break;
  661.             case kControlFocusPrevPart:
  662.             case kControlFocusNextPart:
  663.                 varsp->fInFocus = ( ! varsp->fInFocus);
  664.                 focusResult = varsp->fInFocus ? 1 : kControlFocusNoPart;
  665.                 break;
  666.         }
  667.             /* reactivate the text as necessary */
  668.         TPPaneDrawEntry(tpvars, &ps);
  669.             TPActivatePaneText(tpvars, varsp->fIsActive && varsp->fInFocus);
  670.         TPPaneDrawExit(&ps);
  671.             /* redraw the text fram and focus rectangle to indicate the
  672.             new focus state */
  673.         DrawThemeEditTextFrame(&varsp->fRTextOutline, varsp->fIsActive ? kThemeStateActive: kThemeStateInactive);
  674.         DrawThemeFocusRect(&varsp->fRFocusOutline, varsp->fIsActive && varsp->fInFocus);
  675.             /* done */
  676.         HSetState((Handle) tpvars, state);
  677.     }
  678.     return focusResult;
  679. }
  680.  
  681.  
  682.  
  683. /* STUPOpenControl initializes a user pane control so it will be drawn
  684.     and will behave as a scrolling text edit field inside of a window.
  685.     This routine performs all of the initialization steps necessary,
  686.     except it does not create the user pane control itself.  theControl
  687.     should refer to a user pane control that you have either created
  688.     yourself or extracted from a dialog's control heirarchy using
  689.     the GetDialogItemAsControl routine.  */
  690. OSStatus STUPOpenControl(ControlHandle theControl) {
  691.  
  692.     Rect bounds, destRect;
  693.     WindowPtr theWindow;
  694.     RgnHandle temp;
  695.     STPTextPaneVars **tpvars, *varsp;
  696.         
  697.         /* set up our globals */
  698.     if (gTPDrawProc == NULL) gTPDrawProc = NewControlUserPaneDrawUPP(TPPaneDrawProc);
  699.     if (gTPHitProc == NULL) gTPHitProc = NewControlUserPaneHitTestUPP(TPPaneHitTestProc);
  700.     if (gTPTrackProc == NULL) gTPTrackProc = NewControlUserPaneTrackingUPP(TPPaneTrackingProc);
  701.     if (gTPIdleProc == NULL) gTPIdleProc = NewControlUserPaneIdleUPP(TPPaneIdleProc);
  702.     if (gTPKeyProc == NULL) gTPKeyProc = NewControlUserPaneKeyDownUPP(TPPaneKeyDownProc);
  703.     if (gTPActivateProc == NULL) gTPActivateProc = NewControlUserPaneActivateUPP(TPPaneActivateProc);
  704.     if (gTPFocusProc == NULL) gTPFocusProc = NewControlUserPaneFocusUPP(TPPaneFocusProc);
  705.     if (gTPClickLoopProc == NULL) gTPClickLoopProc = NewTEClickLoopUPP(TPTEClickLoopProc);
  706.         
  707.         /* allocate our private storage */
  708.     tpvars = (STPTextPaneVars **) NewHandleClear(sizeof(STPTextPaneVars));
  709.     SetControlReference(theControl, (long) tpvars);
  710.     HLock((Handle) tpvars);
  711.     varsp = *tpvars;
  712.         /* set the initial settings for our private data */
  713.     varsp->fInFocus = false;
  714.     varsp->fIsActive = true;
  715.     varsp->fTEActive = false;
  716.     varsp->fUserPaneRec = theControl;
  717.     theWindow = varsp->fOwner = GetControlOwner(theControl);
  718.     varsp->fDrawingEnvironment = GetWindowPort(varsp->fOwner);
  719.         /* set up the user pane procedures */
  720.     SetControlData(theControl, kControlEntireControl, kControlUserPaneDrawProcTag, sizeof(gTPDrawProc), &gTPDrawProc);
  721.     SetControlData(theControl, kControlEntireControl, kControlUserPaneHitTestProcTag, sizeof(gTPHitProc), &gTPHitProc);
  722.     SetControlData(theControl, kControlEntireControl, kControlUserPaneTrackingProcTag, sizeof(gTPTrackProc), &gTPTrackProc);
  723.     SetControlData(theControl, kControlEntireControl, kControlUserPaneIdleProcTag, sizeof(gTPIdleProc), &gTPIdleProc);
  724.     SetControlData(theControl, kControlEntireControl, kControlUserPaneKeyDownProcTag, sizeof(gTPKeyProc), &gTPKeyProc);
  725.     SetControlData(theControl, kControlEntireControl, kControlUserPaneActivateProcTag, sizeof(gTPActivateProc), &gTPActivateProc);
  726.     SetControlData(theControl, kControlEntireControl, kControlUserPaneFocusProcTag, sizeof(gTPFocusProc), &gTPFocusProc);
  727.         /* calculate the rectangles used by the control */
  728.     GetControlBounds(theControl, &bounds);
  729.     SetRect(&varsp->fRFocusOutline, bounds.left, bounds.top, bounds.right, bounds.bottom);
  730.     SetRect(&varsp->fRTextOutline, bounds.left, bounds.top, bounds.right, bounds.bottom);
  731.     SetRect(&varsp->fRScrollView, bounds.right-18, bounds.top+2, bounds.right-2, bounds.bottom-2);
  732.     SetRect(&varsp->fRTextArea, bounds.left, bounds.top, bounds.right-17, bounds.bottom);
  733.     SetRect(&varsp->fRTextViewMax, bounds.left+2, bounds.top+2, bounds.right-(18+2), bounds.bottom-2);
  734.         /* calculate the background region for the text.  In this case, it's kindof
  735.         and irregular region because we're setting the scroll bar a little ways inside
  736.         of the text area. */
  737.     RectRgn((varsp->fTextBackgroundRgn = NewRgn()), &varsp->fRTextOutline);
  738.     RectRgn((temp = NewRgn()), &varsp->fRScrollView);
  739.     DiffRgn(varsp->fTextBackgroundRgn, temp, varsp->fTextBackgroundRgn);
  740.     DisposeRgn(temp);
  741.  
  742.         /* create the scroll bar. */
  743.     varsp->fScrollBarRec = NewControl(theWindow, &varsp->fRScrollView, "\p", true, 0, 0, 0, kControlScrollBarLiveProc, (long) tpvars);
  744.         /* allocate our text edit record */
  745.     SetPort(varsp->fDrawingEnvironment);
  746.     destRect = varsp->fRTextViewMax;
  747.     destRect.bottom = destRect.top + 5000;
  748.     varsp->fTextEditRec = TENew(&destRect, &varsp->fRTextViewMax);
  749.     TESetClickLoop(gTPClickLoopProc, varsp->fTextEditRec);
  750.  
  751.         /* unlock our storage */
  752.     HUnlock((Handle) tpvars);
  753.         /* perform final activations and setup for our text field.  Here,
  754.         we assume that the window is going to be the 'active' window. */
  755.     TPActivatePaneText(tpvars, varsp->fIsActive && varsp->fInFocus);
  756.     TPRecalculateTextParams(tpvars, true);
  757.         /* all done */
  758.     return noErr;
  759. }
  760.  
  761.  
  762.  
  763. /* STUPCloseControl deallocates all of the structures allocated
  764.     by STUPOpenControl.  */
  765. OSStatus STUPCloseControl(ControlHandle theControl) {
  766.     STPTextPaneVars **tpvars;
  767.         /* set up locals */
  768.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  769.         /* release our sub records */
  770.     DisposeControl((**tpvars).fScrollBarRec);
  771.     TEDispose((**tpvars).fTextEditRec);
  772.         /* delete our private storage */
  773.     DisposeHandle((Handle) tpvars);
  774.         /* zero the control reference */
  775.     SetControlReference(theControl, 0);
  776.     return noErr;
  777. }
  778.  
  779.  
  780.  
  781.  
  782. /* STUPSetText sets the text that will be displayed inside of the STUP control.
  783.     The text view and the scroll bar are re-drawn appropriately
  784.     to reflect the new text. */
  785. OSStatus STUPSetText(ControlHandle theControl, char* text, long count) {
  786.     STPTextPaneVars **tpvars;
  787.         /* set up locals */
  788.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  789.         /* set the text in the record */
  790.     TESetText(text, count, (**tpvars).fTextEditRec);
  791.         /* recalculate the text view */
  792.     TPRecalculateTextParams(tpvars, true);
  793.     return noErr;
  794. }
  795.  
  796.  
  797. /* STUPSetSelection sets the text selection and autoscrolls the text view
  798.     so either the cursor or the selction is in the view. */
  799. void STUPSetSelection(ControlHandle theControl, short selStart, short selEnd) {
  800.     STPTextPaneVars **tpvars;
  801.     TEHandle hTE;
  802.     STPPaneState ps;
  803.         /* set up our locals */
  804.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  805.     hTE = (**tpvars).fTextEditRec;
  806.         /* and our drawing environment as the operation
  807.         may force a redraw in the text area. */
  808.     TPPaneDrawEntry(tpvars, &ps);
  809.         /* reposition the text so we can see the cursor */
  810.     TEAutoView(true, hTE);
  811.     TESetSelect(selStart, selEnd, hTE);
  812.     TESelView(hTE);
  813.     TEAutoView(false, hTE);
  814.         /* restore the drawing enviroment */
  815.     TPPaneDrawExit(&ps);
  816. }
  817.  
  818.  
  819.  
  820.  
  821.  
  822. /* STUPGetText returns the current text data being displayed inside of
  823.     the STUPControl.  theText is a handle you create and pass to
  824.     the routine.  */
  825. OSStatus STUPGetText(ControlHandle theControl, Handle theText) {
  826.     STPTextPaneVars **tpvars;
  827.     Handle actualText;
  828.     short state;
  829.     OSStatus err;
  830.         /* set up locals */
  831.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  832.         /* retrieve the text handle from the text edit record -- not a copy */
  833.     actualText = (Handle) TEGetText((**tpvars).fTextEditRec);
  834.         /* lock it down and make a copy of it in the handle provided
  835.             in theText parameter */
  836.     state = HGetState(actualText);
  837.     HLock(actualText);
  838.     err = PtrToXHand(*actualText, theText, GetHandleSize(actualText));
  839.     HSetState(actualText, state);
  840.         /* all done */
  841.     return err;
  842. }
  843.  
  844.  
  845.  
  846. /* STUPSetFont allows you to set the text font, size, and style that will be
  847.     used for displaying text in the edit field.  This implementation uses old
  848.     style text edit records for text,  what ever font you specify will affect
  849.     the appearance of all of the text being displayed inside of the STUPControl. */
  850. OSStatus STUPSetFont(ControlHandle theControl, short theFont, short theSize, Style theStyle) {
  851.     STPTextPaneVars **tpvars;
  852.     TEHandle hTE;
  853.     FontInfo fin;
  854.         /* set up our locals */
  855.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  856.     hTE = (**tpvars).fTextEditRec;
  857.         /* set the font information in the text edit record */
  858.     (**hTE).txFont = theFont;
  859.     (**hTE).txFace = theStyle;
  860.     (**hTE).txSize = theSize;
  861.         /* reset the lineHeight and fontAscent fields inside of the text edit field */
  862.     FetchFontInfo(theFont, theSize, theStyle, &fin);
  863.     (**hTE).lineHeight = fin.ascent + fin.descent + fin.leading;
  864.     (**hTE).fontAscent = fin.ascent;
  865.         /* recalculate the appearance of the text edit record on the screen */
  866.     TPRecalculateTextParams(tpvars, true);
  867.         /* all done */
  868.     return noErr;
  869. }
  870.  
  871.  
  872. /* STUPCreateControl creates a new user pane control and then it passes it
  873.     to STUPOpenControl to initialize it as a scrolling text user pane control. */
  874. OSStatus STUPCreateControl(WindowPtr theWindow, Rect *bounds, ControlHandle *theControl) {
  875.     short featurSet;
  876.         /* the following feature set can be specified in CNTL resources by using
  877.         the value 1214.  When creating a user pane control, we pass this value
  878.         in the 'value' parameter. */
  879.     featurSet = kControlSupportsEmbedding | kControlSupportsFocus | kControlWantsIdle
  880.             | kControlWantsActivate | kControlHandlesTracking | kControlHasSpecialBackground
  881.             | kControlGetsFocusOnClick | kControlSupportsLiveFeedback;
  882.         /* create the control */
  883.     *theControl = NewControl(theWindow, bounds, "\p", true, featurSet, 0, featurSet, kControlUserPaneProc, 0);
  884.         /* set up the STUP specific features and data */
  885.     STUPOpenControl(*theControl);
  886.         /* all done.... */
  887.     return noErr;
  888. }
  889.  
  890.  
  891. /* STUPDisposeControl calls STUPCloseControl and then it calls DisposeControl. */
  892. OSStatus STUPDisposeControl(ControlHandle theControl) {
  893.         /* deallocate the STUP specific data */
  894.     STUPCloseControl(theControl);
  895.         /* deallocate the user pane control itself */
  896.     DisposeControl(theControl);
  897.     return noErr;
  898. }
  899.  
  900.  
  901.  
  902. /* STUPFillControl looks for a 'STUP' resource.  If it finds one, then
  903.     it sets the font and text in the STUP Control using the parameters
  904.     specified in the 'STUP' resource. */
  905. OSStatus STUPFillControl(ControlHandle theControl, short STUPrsrcID) {
  906.     TextFieldSetupResource **stup;
  907.     Handle theText;
  908.     char state;
  909.         /* get the STUP resource from the resource file */
  910.     stup = (TextFieldSetupResource **) GetResource(kSTUPResourceType, STUPrsrcID);
  911.     if (stup == NULL) return resNotFound;
  912.         /* get a handle to the text resource */
  913.     theText = GetResource(kTEXTResourceType, (**stup).textresourceID);
  914.     if (theText == NULL) return resNotFound;
  915.         /* set the font */
  916.     STUPSetFont(theControl, (**stup).theFont, (**stup).theSize, (**stup).theStyle);
  917.         /* set the text */
  918.     state = HGetState(theText);
  919.     HLock(theText);
  920.     STUPSetText(theControl, *theText, GetHandleSize(theText));
  921.     HSetState(theText, state);
  922.         /* place the cursor at the top */
  923.     STUPSetSelection(theControl, 0, 0);
  924.         /* we're done */
  925.     return noErr;
  926. }
  927.  
  928.  
  929.  
  930.  
  931. /* IsSTUPControl returns true if theControl is not NULL
  932.     and theControl refers to a STUP Control.  */
  933. Boolean IsSTUPControl(ControlHandle theControl) {
  934.     Size theSize;
  935.     ControlUserPaneFocusUPP localFocusProc;
  936.         /* a NULL control is not a STUP control */
  937.     if (theControl == NULL) return false;
  938.         /* check if the control is using our focus procedure */
  939.     theSize = sizeof(localFocusProc);
  940.     if (GetControlData(theControl, kControlEntireControl, kControlUserPaneFocusProcTag,
  941.         sizeof(localFocusProc), &localFocusProc, &theSize) != noErr) return false;
  942.     if (localFocusProc != gTPFocusProc) return false;
  943.         /* all tests passed, it's a STUP control */
  944.     return true;
  945. }
  946.  
  947.  
  948. /* STUPDoEditCommand performs the editing command specified
  949.     in the editCommand parameter.  The STUPControl's text
  950.     and scroll bar are redrawn and updated as necessary. */
  951. void STUPDoEditCommand(ControlHandle theControl, short editCommand) {
  952.     STPTextPaneVars **tpvars;
  953.     TEHandle hTE;
  954.     STPPaneState ps;
  955.         /* set up our locals */
  956.     tpvars = (STPTextPaneVars **) GetControlReference(theControl);
  957.     hTE = (**tpvars).fTextEditRec;
  958.         /* and our drawing environment as the operation
  959.         may force a redraw in the text area. */
  960.     TPPaneDrawEntry(tpvars, &ps);
  961.         /* perform the editing command */
  962.     switch (editCommand) {
  963.         case kSTUPCut:
  964.             ClearCurrentScrap();
  965.             TECut(hTE); 
  966.             TEToScrap();
  967.             break;
  968.         case kSTUPCopy:
  969.             ClearCurrentScrap();
  970.             TECopy(hTE);
  971.             TEToScrap();
  972.             break;
  973.         case kSTUPPaste:
  974.             TEFromScrap();
  975.             TEPaste(hTE);
  976.             break;
  977.         case kSTUPClear:
  978.             TEDelete(hTE);
  979.             break;
  980.     }
  981.         /* reposition the text so we can see the cursor */
  982.     TEAutoView(true, (**tpvars).fTextEditRec);
  983.     TESelView((**tpvars).fTextEditRec);
  984.     TEAutoView(false, (**tpvars).fTextEditRec);
  985.         /* restore the drawing enviroment */
  986.     TPPaneDrawExit(&ps);
  987.         /* reposition the scroll bar */
  988.     TPRecalculateTextParams(tpvars, false);
  989. }
  990.  
  991.